﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

using System.IO;
using System.Runtime.Serialization;
using System.Runtime.Serialization.Formatters.Binary;
using System.Threading;

namespace VDataAdapter
{
    public class PriceServerObjectFile
    {
        protected Dictionary<string, ReaderWriterLock> _lockList = new Dictionary<string, ReaderWriterLock>();
        protected ReaderWriterLockSlim _lock = new ReaderWriterLockSlim();

        public PriceServerObjectFile()
        {}

        // this is an atomic function
        public void WriteToFile(string filepath, Object[] objs)
        {
            EnterWriteLock(filepath);
            try
            {
                Stream stream = InitFileWriteStream(filepath);
                BinaryFormatter formatter = new BinaryFormatter();
                foreach (Object obj in objs)
                {
                    formatter.Serialize(stream, obj);
                }
                stream.Close();

                // Sleep for a while in order for file to be closed
                Thread.Sleep(5);
            }
            catch (Exception ex)
            {
                throw new Exception("PriceServerObjectFile Exception");
            }
            ReleaseLock(filepath);
        }

        public Object[] ReadFromFile(string filepath)
        {
            Stream stream = InitFileReadStream(filepath);

            List<Object> result = new List<Object>();

            while (stream.Position < stream.Length)
            {
                result.Add(ReadFromStream(stream));
            }

            return result.ToArray();
        }

        /// <summary>
        /// identical to ReadFromFile, but is static method
        /// provide a different methdo so that we dont have to edit all other affacted class
        /// </summary>
        /// <param name="filepath"></param>
        /// <returns></returns>
        public static Object[] LoadFromFile(string filepath)
        {
            var reader = new PriceServerObjectFile();
            return reader.ReadFromFile(filepath);
        }

        public Object ReadFromStream(Stream stream)
        {
            BinaryFormatter formatter = new BinaryFormatter();
            return formatter.Deserialize(stream);
        }

        private Stream InitFileReadStream(string filepath)
        {
            Stream stream;
            try
            {
                if (!File.Exists(filepath))
                    throw new IOException("File does not exist: " + filepath);

                stream = File.Open(filepath, FileMode.Open);
            }
            catch (IOException ex)
            {
                throw new IOException("There is an error accessing file: " + ex.Message);
            }

            return stream;
        }

        private Stream InitFileWriteStream(string filepath)
        {
            Stream stream;
            try
            {
                if (!File.Exists(filepath))
                    stream = File.Open(filepath, FileMode.Create);
                else
                {
                    stream = File.Open(filepath, FileMode.Append);
                }
            }
            catch (IOException ex)
            {
                throw new IOException("There is an error accessing file: " + ex.Message);
            }

            return stream;
        }

        private void ReleaseLock(string filepath)
        {
            if (_lockList.ContainsKey(filepath))
            {
                _lockList[filepath].ReleaseWriterLock();
            }
        }

        private void EnterWriteLock(string filepath)
        {
            if (!_lockList.ContainsKey(filepath))
            {
                _lockList[filepath] = new ReaderWriterLock();
            }

            try
            {
                _lockList[filepath].AcquireWriterLock(10000);                 
            }
            catch (ApplicationException ex)
            {
                throw new ApplicationException("Wait too long for the lock" + " for " + filepath + ex.Message);
            }
        }

        
    }
}
